﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace SimpleModbus
{

    /// <summary>
    /// ModbusTCP通讯类，基于Socket实现
    /// </summary>
    public class ModbusTCP : IDisposable
    {
        /// <summary>
        /// socket
        /// </summary>
        private Socket tcpClient;

        /// <summary>
        /// 发送缓冲区大小,默认值为512
        /// </summary>
        public int SendBufferSize { get; set; } = 512;

        /// <summary>
        /// scoket收发延时
        /// </summary>
        public int SendTimeOut { get; set; } = 2000;
        public int RevieveTimeOut { get; set; } = 2000;

        /// <summary>
        /// 接收报文时最大问询次数
        /// </summary>
        public int MaxAttempts { get; set; } = 5;

        /// <summary>
        /// 数据格式解析类型
        /// </summary>
        public DataFormat DataFormat { get; set; } = DataFormat.ABCD;//默认与ModbusSlave对齐

        /// <summary>
        /// 互斥锁对象
        /// </summary>
        private readonly object _lock = new object();

        /// <summary>
        /// 信号量
        /// </summary>
        private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);//定义一个 SemaphoreSlim 对象，初始计数为 1，最大计数为 1，确保同一时间只有一个线程可以访问临界区。

        /// <summary>
        /// 同步方式建立tcp网络连接
        /// </summary>
        /// <param name="ip">ip地址</param>
        /// <param name="port">端口号</param>
        public void Connect(string ip, string port)
        {
            try
            {
                //实例化并配置
                tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//参数：ipv4/ipv4，双向字节流方式传输，tcp协议

                //端点：连接信息
                IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse(ip), int.Parse(port));

                //设置收发延时断联
                tcpClient.SendTimeout = this.SendTimeOut;
                tcpClient.ReceiveTimeout = this.RevieveTimeOut;

                //建立连接
                tcpClient.Connect(iPEndPoint);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 异步方式建立tcp网络连接
        /// </summary>
        /// <param name="ip">ip地址</param>
        /// <param name="port">端口号</param>
        public async Task ConnectAsync(string ip, string port)
        {
            try
            {
                //实例化并配置
                tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//参数：ipv4/ipv4，双向字节流方式传输，tcp协议

                //端点：连接信息
                IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse(ip), int.Parse(port));

                //设置收发延时断联
                tcpClient.SendTimeout = this.SendTimeOut;
                tcpClient.ReceiveTimeout = this.RevieveTimeOut;

                //建立连接
                await tcpClient.ConnectAsync(iPEndPoint);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            try
            {
                tcpClient?.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
                throw;
            }
        }

        /// <summary>
        /// IDisposable接口实现
        /// </summary>
        public void Dispose()
        {
            // 释放Socket资源
            if (tcpClient != null)
            {
                tcpClient.Close();
                tcpClient.Dispose();
                tcpClient = null;
            }
        }

        /// <summary>
        /// 同步方式读取线圈
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="ICount">读取线圈的数量</param>
        /// <param name="ISlaveAddress">单元标识符，从站设备地址(默认1)</param>
        public byte[] ReadOutputStatus(int IAddress, int ICount, int ISlaveAddress = 1)//可选参数必须在函数参数的最后边
        {
            //实例化字节数组
            ByteArray byteArray = new ByteArray();

            //1.拼接报文
            byteArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务、协议、字节长度标识符
            byteArray.Add((byte)ISlaveAddress);//从站地址
            byteArray.Add(new byte[] { 0x01, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址与功能码
            byteArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//线圈数量



            //4.验证报文:验证字节计数与数据部分字节数是否相同
            int length = 0;
            if (ICount % 8 == 0)
                length = ICount / 8;
            else
                length = ICount / 8 + 1;

            //接收报文
            var rcvBuffer = SendAndReceiveMsg(byteArray.Array);

            if (rcvBuffer[8] == length)//隐式转换：字节数组本质是0-255的无符号数
            {
                //5.解析
                return GetByteArray(rcvBuffer, 9, length);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 异步方式读取线圈
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="ICount">读取线圈的数量</param>
        /// <param name="ISlaveAddress">单元标识符，从站设备地址(默认1)</param>
        /// <returns>线圈状态值数组</returns>
        public async Task<byte[]> ReadOutputStatusAsync(int IAddress, int ICount, int ISlaveAddress = 1)
        {
            //实例化字节数组
            ByteArray byteArray = new ByteArray();

            //1.拼接报文
            byteArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务、协议、字节长度标识符
            byteArray.Add((byte)ISlaveAddress);//从站地址
            byteArray.Add(new byte[] { 0x01, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址与功能码
            byteArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//线圈数量



            //4.验证报文:验证字节计数与数据部分字节数是否相同
            var length = GetStatusLength(ICount);

            //接收报文
            var rcvBuffer = await SendAndReceiveMsgAsync(byteArray.Array, 9 + length);

            if (rcvBuffer[8] == length)//隐式转换：字节数组本质是0-255的无符号数
            {
                //5.解析
                return GetByteArray(rcvBuffer, 9, length);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 同步方式读取离散输入
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="ICount">读取线圈的数量</param>
        /// <param name="ISlaveAddress">单元标识符，从站设备地址(默认1)</param>
        public byte[] ReadInputStatus(int IAddress, int ICount, int ISlaveAddress = 1)
        {
            //实例化字节数组
            ByteArray byteArray = new ByteArray();

            //1.拼接报文
            byteArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务、协议、字节长度标识符
            byteArray.Add((byte)ISlaveAddress);//从站地址
            byteArray.Add(new byte[] { 0x02, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址与功能码
            byteArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//线圈数量



            //4.验证报文:验证字节计数与数据部分字节数是否相同
            int length = 0;
            if (ICount % 8 == 0)
                length = ICount / 8;
            else
                length = ICount / 8 + 1;

            //接收报文
            var rcvBuffer = SendAndReceiveMsg(byteArray.Array);
            if (rcvBuffer != null && rcvBuffer.Length == 9 + length)
            {
                if (rcvBuffer[8] == length)//隐式转换：字节数组本质是0-255的无符号数
                {
                    //5.解析
                    return GetByteArray(rcvBuffer, 9, length);
                }
                else
                {
                    return null;
                }
            }
            return null;
        }

        /// <summary>
        /// 异步方式读取离散输入
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="ICount">读取线圈的数量</param>
        /// <param name="ISlaveAddress">单元标识符，从站设备地址(默认1)</param>
        public async Task<byte[]> ReadInputStatusAsync(int IAddress, int ICount, int ISlaveAddress = 1)
        {
            //实例化字节数组
            ByteArray byteArray = new ByteArray();

            //1.拼接报文
            byteArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务、协议、字节长度标识符
            byteArray.Add((byte)ISlaveAddress);//从站地址
            byteArray.Add(new byte[] { 0x02, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址与功能码
            byteArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//线圈数量



            //4.验证报文:验证字节计数与数据部分字节数是否相同
            var length = GetStatusLength(ICount);

            //接收报文
            var rcvBuffer = await SendAndReceiveMsgAsync(byteArray.Array, 9 + length);

            if (rcvBuffer[8] == length)//隐式转换：字节数组本质是0-255的无符号数
            {
                //5.解析
                return GetByteArray(rcvBuffer, 9, length);
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// 同步方式读取保持寄存器
        /// </summary>
        /// <param name="IAddress">起始寄存器地址</param>
        /// <param name="ICount">读取的寄存器数量：modbus协议中一个寄存器为16位，即两字节，在变量类型为short时，1数据量需读取1寄存器、2字节表示，为int32时，1数据量需2连续寄存器、4字节表示</param>
        /// <param name="ISlaveAddress">从站地址，默认为1</param>
        /// <returns></returns>
        public byte[] ReadHoldRegisters(int IAddress, int ICount, int ISlaveAddress = 1)
        {
            ByteArray sendPackArray = new ByteArray();

            sendPackArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务协议以及字节长度
            sendPackArray.Add((byte)ISlaveAddress);//从站
            sendPackArray.Add(new byte[] { 0x03, (byte)(IAddress / 256), (byte)(IAddress % 256) });//功能码、其实寄存器地址
            sendPackArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//读取寄存器的数量

            var rcv = SendAndReceiveMsg(sendPackArray.Array);

            //验证报文完整性
            if (rcv != null && rcv.Length == 9 + ICount * 2)
            {
                //验证报文细节
                if (rcv[8] == ICount * 2 && rcv[7] == 0x03)
                {
                    return GetByteArray(rcv, 9, ICount * 2);
                }
                else
                    return null;

            }
            return null;
        }

        /// <summary>
        /// 异步方式读取保持寄存器
        /// </summary>
        /// <param name="IAddress">起始寄存器地址</param>
        /// <param name="ICount">读取的寄存器数量</param>
        /// <param name="ISlaveAddress">从站地址，默认为1</param>
        /// <returns></returns>
        public async Task<byte[]> ReadHoldRegistersAsync(int IAddress, int ICount, int ISlaveAddress = 1)
        {
            ByteArray sendPackArray = new ByteArray();

            sendPackArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务协议以及字节长度
            sendPackArray.Add((byte)ISlaveAddress);//从站
            sendPackArray.Add(new byte[] { 0x03, (byte)(IAddress / 256), (byte)(IAddress % 256) });//功能码、其实寄存器地址
            sendPackArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//读取寄存器的数量

            var rcv = await SendAndReceiveMsgAsync(sendPackArray.Array, 9 + ICount * 2);

            //验证报文完整性
            if (rcv != null && rcv.Length == 9 + ICount * 2)
            {
                //验证报文细节
                if (rcv[8] == ICount * 2 && rcv[7] == 0x03)
                {
                    return GetByteArray(rcv, 9, ICount * 2);
                }
                else
                    return null;

            }
            return null;
        }

        /// <summary>
        /// 同步方式读取输入寄存器
        /// </summary>
        /// <param name="IAddress">起始寄存器地址</param>
        /// <param name="ICount">读取的寄存器数量</param>
        /// <param name="ISlaveAddress">从站地址，默认为1</param>
        /// <returns></returns>
        public byte[] ReadInputRegisters(int IAddress, int ICount, int ISlaveAddress = 1)
        {
            ByteArray sendPackArray = new ByteArray();

            sendPackArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务协议以及字节长度
            sendPackArray.Add((byte)ISlaveAddress);//从站
            sendPackArray.Add(new byte[] { 0x04, (byte)(IAddress / 256), (byte)(IAddress % 256) });//功能码、其实寄存器地址
            sendPackArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//读取寄存器的数量

            var rcv = SendAndReceiveMsg(sendPackArray.Array);

            //验证报文完整性
            if (rcv != null && rcv.Length == 9 + ICount * 2)
            {
                //验证报文细节
                if (rcv[8] == ICount * 2 && rcv[7] == 0x04)
                {
                    return GetByteArray(rcv, 9, ICount * 2);
                }
                else
                    return null;

            }
            return null;
        }

        /// <summary>
        /// 异步方式读取输入寄存器
        /// </summary>
        /// <param name="IAddress">起始寄存器地址</param>
        /// <param name="ICount">读取的寄存器数量</param>
        /// <param name="ISlaveAddress">从站地址，默认为1</param>
        /// <returns></returns>
        public async Task<byte[]> ReadInputRegistersAsync(int IAddress, int ICount, int ISlaveAddress = 1)
        {
            ByteArray sendPackArray = new ByteArray();

            sendPackArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务协议以及字节长度
            sendPackArray.Add((byte)ISlaveAddress);//从站
            sendPackArray.Add(new byte[] { 0x04, (byte)(IAddress / 256), (byte)(IAddress % 256) });//功能码、其实寄存器地址
            sendPackArray.Add(new byte[] { (byte)(ICount / 256), (byte)(ICount % 256) });//读取寄存器的数量

            var rcv = await SendAndReceiveMsgAsync(sendPackArray.Array, ICount * 2);

            //验证报文完整性
            if (rcv != null && rcv.Length == 9 + ICount * 2)
            {
                //验证报文细节
                if (rcv[8] == ICount * 2 && rcv[7] == 0x04)
                {
                    return GetByteArray(rcv, 9, ICount * 2);
                }
                else
                    return null;

            }
            return null;
        }

        /// <summary>
        /// 同步方式实现强制单线圈
        /// </summary>
        /// <param name="IAddress">线圈地址</param>
        /// <param name="ICoilState">断通标志</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>应答报文是否与问询报文相同</returns>
        public bool ForceSingleCoil(int IAddress, bool ICoilState, int ISlaveAddress = 1)
        {
            ByteArray byteArray = new ByteArray();

            byteArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });
            byteArray.Add((byte)ISlaveAddress);
            byteArray.Add(new byte[] { 0x05, (byte)(IAddress / 256), (byte)(IAddress % 256) });

            //拼装断通标志
            if (ICoilState)
            {
                byteArray.Add(new byte[] { 0xff, 0x00 });//置位
            }
            else
            {
                byteArray.Add(new byte[] { 0x00, 0x00 });//复位
            }


            var rcv = SendAndReceiveMsg(byteArray.Array);

            if (rcv != null)
            {
                return IsByteArrayEquals(rcv, byteArray.Array);
            }
            else
                return false;
        }

        /// <summary>
        /// 异步方式实现强制单线圈
        /// </summary>
        /// <param name="IAddress">线圈地址</param>
        /// <param name="ICoilState">断通标志</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>应答报文是否与问询报文相同</returns>
        public async Task<bool> ForceSingleCoilAsync(int IAddress, bool ICoilState, int ISlaveAddress = 1)
        {
            ByteArray byteArray = new ByteArray();

            byteArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });
            byteArray.Add((byte)ISlaveAddress);
            byteArray.Add(new byte[] { 0x05, (byte)(IAddress / 256), (byte)(IAddress % 256) });

            //拼装断通标志
            if (ICoilState)
            {
                byteArray.Add(new byte[] { 0xff, 0x00 });//置位
            }
            else
            {
                byteArray.Add(new byte[] { 0x00, 0x00 });//复位
            }


            var rcv = await SendAndReceiveMsgAsync(byteArray.Array, byteArray.Array.Length);

            if (rcv != null)
            {
                return IsByteArrayEquals(rcv, byteArray.Array);
            }
            else
                return false;
        }

        /// <summary>
        /// 同步方式实现强制多线圈
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="ICoilStateArray">线圈状态数组</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>应答报文合法性结果</returns>
        public bool ForceMultiCoil(int IAddress, bool[] ICoilStateArray, int ISlaveAddress = 1)
        {
            ByteArray sendArray = new ByteArray();

            var byteArray = BoolArrayToByteArray(ICoilStateArray);

            //组装发送报文    
            sendArray.Add(new byte[] { 0, 0, 0, 0 });//事务、协议
            sendArray.Add((byte)(7 + byteArray.Length));//字节长度标识符
            sendArray.Add(new byte[] { (byte)ISlaveAddress, 0x0f });//单元标识符、功能码
            sendArray.Add(new byte[] { 0x05, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址
            sendArray.Add(new byte[] { 0x05, (byte)(ICoilStateArray.Length / 256), (byte)(ICoilStateArray.Length % 256) });//线圈数量
            sendArray.Add((byte)byteArray.Length);//字节计数
            for (int i = 0; i < byteArray.Length; i++)
            {
                sendArray.Add(byteArray[i]);//字节
            }

            var rcv = SendAndReceiveMsg(sendArray.Array);

            if (rcv != null)
            {
                //验证报文细节
                byte[] send = GetByteArray(sendArray.Array, 0, 12);
                send[4] = 0x00;
                send[5] = 0x06;

                return IsByteArrayEquals(send, rcv);
            }
            else
                return false;
        }

        /// <summary>
        /// 异步方式实现强制多线圈
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="ICoilStateArray">线圈状态数组</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>应答报文合法性结果</returns>
        public async Task<bool> ForceMultiCoilAsync(int IAddress, bool[] ICoilStateArray, int ISlaveAddress = 1)
        {
            ByteArray sendArray = new ByteArray();

            var byteArray = BoolArrayToByteArray(ICoilStateArray);

            //组装发送报文    
            sendArray.Add(new byte[] { 0, 0, 0, 0 });//事务、协议
            sendArray.Add(new byte[] { 0x00, (byte)(7 + byteArray.Length) });//字节长度标识符
            sendArray.Add(new byte[] { (byte)ISlaveAddress, 0x0f });//单元标识符、功能码
            sendArray.Add(new byte[] { (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址
            sendArray.Add(new byte[] { (byte)(ICoilStateArray.Length / 256), (byte)(ICoilStateArray.Length % 256) });//线圈数量
            sendArray.Add((byte)byteArray.Length);//字节计数
            for (int i = 0; i < byteArray.Length; i++)
            {
                sendArray.Add(byteArray[i]);//字节
            }

            var rcv = await SendAndReceiveMsgAsync(sendArray.Array, 12);
            if (rcv != null)
            {
                //验证报文细节
                byte[] send = GetByteArray(sendArray.Array, 0, 12);
                send[4] = 0x00;
                send[5] = 0x06;

                return IsByteArrayEquals(send, rcv);
            }
            else
                return false;
        }

        /// <summary>
        /// 同步方式实现强制单寄存器
        /// </summary>
        /// <param name="IAddress">寄存器地址</param>
        /// <param name="IValue">寄存器值</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns></returns>
        public bool ForceSingleRegister(int IAddress, short IValue, int ISlaveAddress = 1)
        {
            ByteArray sendArray = new ByteArray();

            //组装发送报文
            sendArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务、协议、字节长度标识符
            sendArray.Add((byte)ISlaveAddress);//从站地址
            sendArray.Add(new byte[] { 0x06, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址与功能码
            sendArray.Add(ModbusFormatTool.GetByteArrayFrom16Bit(IValue, this.DataFormat));//寄存器值

            //发送报文
            var rcv = SendAndReceiveMsg(sendArray.Array);

            return rcv == null ? throw new Exception() : IsByteArrayEquals(sendArray.Array, rcv);
        }

        /// <summary>
        /// 异步方式实现强制单寄存器
        /// </summary>
        /// <param name="IAddress">寄存器地址</param>
        /// <param name="IValue">寄存器值</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns></returns>
        public async Task<bool> ForceSingleRegisterAsync(int IAddress, short IValue, int ISlaveAddress = 1)
        {
            ByteArray sendArray = new ByteArray();

            //组装发送报文
            sendArray.Add(new byte[] { 0, 0, 0, 0, 0, 6 });//事务、协议、字节长度标识符
            sendArray.Add((byte)ISlaveAddress);//从站地址
            sendArray.Add(new byte[] { 0x06, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址与功能码
            sendArray.Add(ModbusFormatTool.GetByteArrayFrom16Bit(IValue, this.DataFormat));//寄存器值

            //发送报文
            var rcv = await SendAndReceiveMsgAsync(sendArray.Array, sendArray.Array.Length);

            return rcv == null ? throw new Exception() : IsByteArrayEquals(sendArray.Array, rcv);
        }

        /// <summary>
        /// 同步方式实现强制多寄存器
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="IValue">short类型数据</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>是否强制成功</returns>
        public bool ForceMultiRegister(int IAddress, short[] IValue, int ISlaveAddress = 1)
        {
            ByteArray byteArray = new ByteArray();
            foreach (var item in IValue)
            {
                byteArray.Add(ModbusFormatTool.GetByteArrayFrom16Bit(item, DataFormat));
            }

            return ForceMultiArray(IAddress, byteArray.Array, ISlaveAddress);
        }

        /// <summary>
        /// 同步方式实现强制多寄存器
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="IValue">ushort类型数据</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>是否强制成功</returns>
        public bool ForceMultiRegister(int IAddress, ushort[] IValue, int ISlaveAddress = 1)
        {
            ByteArray byteArray = new ByteArray();
            foreach (var item in IValue)
            {
                byteArray.Add(ModbusFormatTool.GetByteArrayFrom16Bit(item, DataFormat));
            }

            return ForceMultiArray(IAddress, byteArray.Array, ISlaveAddress);
        }

        /// <summary>
        /// 同步方式实现强制多寄存器
        /// </summary>
        /// <param name="IAddress"></param>
        /// <param name="IValue"></param>
        /// <param name="ISlaveAddress"></param>
        /// <returns></returns>
        public bool ForceMultiRegister(int IAddress, int[] IValue, int ISlaveAddress = 1)
        {
            ByteArray byteArray = new ByteArray();
            foreach (var item in IValue)
            {
                byteArray.Add(ModbusFormatTool.GetByteArrayFrom32Bit(item, DataFormat));
            }

            return ForceMultiArray(IAddress, byteArray.Array, ISlaveAddress);
        }

        /// <summary>
        /// 同步方式实现强制多寄存器
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="IValue">uint类型数据</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>是否强制成功</returns>
        public bool ForceMultiRegister(int IAddress, uint[] IValue, int ISlaveAddress = 1)
        {
            ByteArray byteArray = new ByteArray();
            foreach (var item in IValue)
            {
                byteArray.Add(ModbusFormatTool.GetByteArrayFrom32Bit(item, DataFormat));
            }

            return ForceMultiArray(IAddress, byteArray.Array, ISlaveAddress);
        }

        /// <summary>
        /// 同步方式实现强制多寄存器
        /// </summary>
        /// <param name="IAddress">起始地址</param>
        /// <param name="IValue">float类型数据</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>是否强制成功</returns>
        public bool ForceMultiRegister(int IAddress, float[] IValue, int ISlaveAddress = 1)
        {
            ByteArray byteArray = new ByteArray();
            foreach (var item in IValue)
            {
                byteArray.Add(ModbusFormatTool.GetByteArrayFrom32Bit(item, DataFormat));
            }

            return ForceMultiArray(IAddress, byteArray.Array, ISlaveAddress);
        }

        /// <summary>
        /// 同步方式实现强制多寄存器的基础方法
        /// </summary>
        /// <param name="IAddress">起始modbus地址，注：在32b数据中，起始modbus地址如偏移出错，则可能造成数据偏移</param>
        /// <param name="valueArray">字节数组</param>
        /// <param name="ISlaveAddress">从站地址</param>
        /// <returns>是否修改成功</returns>
        private bool ForceMultiArray(int IAddress, byte[] valueArray, int ISlaveAddress = 1)
        {
            ByteArray sendArray = new ByteArray();

            //组装发送报文
            sendArray.Add(new byte[] { 0, 0, 0, 0 });//事务、协议
            sendArray.Add(new byte[] { (byte)((7 + valueArray.Length) / 256), (byte)((7 + valueArray.Length) % 256) });//字节长度标识符
            sendArray.Add((byte)ISlaveAddress);//从站地址
            sendArray.Add(new byte[] { 0x10, (byte)(IAddress / 256), (byte)(IAddress % 256) });//起始地址与功能码
            sendArray.Add(new byte[] { (byte)(valueArray.Length / 256 / 2), (byte)(valueArray.Length % 256 / 2) });//寄存器数量
            sendArray.Add((byte)valueArray.Length);//字节计数
            sendArray.Add(valueArray);//寄存器字节值

            //发送报文
            var rcv = SendAndReceiveMsg(sendArray.Array);

            if (rcv != null)
            {
                rcv[4] = (byte)((7 + valueArray.Length) / 256);
                rcv[5] = (byte)((7 + valueArray.Length) % 256);
            }

            return rcv == null ? throw new Exception() : IsByteArrayEquals(GetByteArray(sendArray.Array, 0, 12), rcv);
        }

        #region 工具方法

        /// <summary>
        /// 自定义截取数组
        /// </summary>
        /// <param name="array">源数组</param>
        /// <param name="offset">开始下标</param>
        /// <param name="length">截取长度</param>
        /// <returns></returns>
        private byte[] GetByteArray(byte[] array, int offset, int length)
        {
            byte[] result = new byte[length];

            //数组拷贝
            if (array.Length >= offset + length)
            {
                Array.Copy(array, offset, result, 0, length);
                return result;
            }
            else
            {
                return null;
            }



        }

        /// <summary>
        /// 判断字节数组是否与另一数组相同
        /// </summary>
        /// <returns></returns>
        public bool IsByteArrayEquals(byte[] b1, byte[] b2)
        {
            if (b1 == null || b2 == null) return false;
            if (b1.Length != b2.Length) return false;

            for (int i = 0; i < b1.Length; i++)
                if (b1[i] != b2[i])
                    return false;

            return true;
        }

        /// <summary>
        /// 将bool数组转换为字节数组
        /// </summary>
        public byte[] BoolArrayToByteArray(bool[] boolArray)
        {
            int byteCount = 0;//字节数

            //计算所需字节数
            byteCount = boolArray.Length % 8 == 0 ? boolArray.Length / 8 : boolArray.Length / 8 + 1;

            //在 C# 中，当你使用 new byte[byteCount] 创建一个字节数组时，数组中的所有字节都会被初始化为 0
            var byteArray = new byte[byteCount];

            for (int i = 0; i < byteCount; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    if (i * 8 + j < boolArray.Length)
                    {
                        //将bool值转换为字节
                        if (boolArray[i * 8 + j])//判断是否是1
                        {
                            //每个字节内部的位排序也遵循同样的MSB优先原则:位置(索引): 7  6  5  4  3  2  1  0
                            //按位或将对应位置的值设置为1
                            byteArray[i] |= (byte)(1 << j);
                        }
                    }
                }
            }

            return byteArray;
        }

        /// <summary>
        /// 通过线圈数量获取返回字节长度
        /// </summary>
        /// <param name="ICount"></param>
        /// <returns></returns>
        private int GetStatusLength(int ICount)
        {
            int length = 0;
            if (ICount % 8 == 0)
                length = ICount / 8;
            else
                length = ICount / 8 + 1;
            return length;
        }

        /// <summary>
        /// 同步方式发送并接收报文
        /// </summary>
        /// <param name="array">字节数组形式的报文</param>
        /// <returns>应答报文</returns>
        private byte[] SendAndReceiveMsg(byte[] array)
        {
            byte[] rcvBuffer = new byte[SendBufferSize];

            //加锁，避免多线程同时访问产生粘包问题
            lock (_lock)
            {
                try
                {
                    //2.发送报文
                    tcpClient.Send(array, SocketFlags.None);

                    //3.接收报文
                    rcvBuffer = ReceiveMsg();
                }
                catch (SocketException ex)
                {
                    Console.WriteLine($"Error: {ex.Message}");
                    rcvBuffer = null;
                }
            }
            return rcvBuffer;
        }

        /// <summary>
        /// 异步方式发送并接收报文
        /// </summary>
        /// <param name="array">字节数组形式的报文</param>
        /// <param name="expectedLength">期待返回字节的长度</param>
        /// <returns>应答报文</returns>
        private async Task<byte[]> SendAndReceiveMsgAsync(byte[] array, int expectedLength)
        {
            byte[] rcvBuffer = new byte[SendBufferSize];

            await _semaphore.WaitAsync();//加锁，避免多线程同时访问产生粘包问题

            try
            {
                //2.发送报文
                await tcpClient.SendAsync(new ArraySegment<byte>(array), SocketFlags.None);

                //3.接收报文
                rcvBuffer = await ReceiveMsgAsync(expectedLength);
            }
            catch (SocketException ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
                rcvBuffer = null;
            }
            finally
            {
                _semaphore.Release();//释放锁
            }

            return rcvBuffer;
        }

        /// <summary>
        /// 同步方式接收报文
        /// </summary>
        /// <returns>接收的报文数据</returns>
        private byte[] ReceiveMsg()
        {
            int count = tcpClient.Available;//读取接收缓冲区中的字节数
            int attempts = 0;//问询次数

            //循环等待数据到达
            while (count == 0 && attempts < MaxAttempts)
            {
                count = tcpClient.Available;
                if (count == 0)
                {
                    Thread.Sleep(20);//避免UI线程阻塞
                    attempts++;
                }
            }

            if (count == 0)//未获得数据
            {
                return null;
            }
            else
            {
                byte[] buffer = new byte[count];
                try
                {
                    //接收报文：ArraySegment<byte>(buffer)表示数组的子集，无需创建额外数组，亦可控制精准分配，默认为整个数组
                    var rcvCount = tcpClient.Receive(buffer, SocketFlags.None);

                    //实际接收的数值是否等同于count值
                    if (rcvCount != count)
                    {
                        return null;
                    }
                }
                catch (SocketException ex)
                {
                    Console.WriteLine($"Error: {ex.Message}");
                    return null;
                }
                return buffer;
            }
        }

        /// <summary>
        /// 自定义异步方式接收报文
        /// </summary>
        /// <returns>接收的报文数据</returns>
        private async Task<byte[]> ReceiveMsgAsync()
        {
            int count = tcpClient.Available;//读取接收缓冲区中的字节数
            int attempts = 0;//问询次数

            //循环等待数据到达
            while (count == 0 && attempts < MaxAttempts)
            {
                count = tcpClient.Available;//是不可靠的，因为数据可能分多次到达
                if (count == 0)
                {
                    await Task.Delay(20);//避免UI线程阻塞
                    attempts++;
                }
            }

            if (count == 0)//未获得数据
            {
                return null;
            }
            else
            {
                byte[] buffer = new byte[count];
                try
                {
                    //接收报文：ArraySegment<byte>(buffer)表示数组的子集，无需创建额外数组，亦可控制精准分配，默认为整个数组
                    var rcvCount = await tcpClient.ReceiveAsync(new ArraySegment<byte>(buffer), SocketFlags.None);

                    //实际接收的数值是否等同于count值
                    if (rcvCount != count)
                    {
                        return null;
                    }
                }
                catch (SocketException ex)
                {
                    Console.WriteLine($"Error: {ex.Message}");
                    return null;
                }
                return buffer;
            }
        }

        /// <summary>
        /// 自定义异步方式接收报文，通过应答报文期望字节长度接收
        /// </summary>
        /// <param name="expectedLength">期望应答报文的字节长度</param>
        /// <returns>应答报文</returns>
        private async Task<byte[]> ReceiveMsgAsync(int expectedLength)
        {
            byte[] buffer = new byte[expectedLength];
            int totalReceived = 0;
            int attempts = 0;

            while (totalReceived < expectedLength && attempts < MaxAttempts)//可靠，数据分多次到达依旧可以获得数据
            {
                try
                {
                    int received = await tcpClient.ReceiveAsync(
                        new ArraySegment<byte>(buffer, totalReceived, expectedLength - totalReceived),
                        SocketFlags.None
                    );//分段接收覆盖


                    if (received == 0)//如果读不出来任何数据直接关闭
                    {
                        // 连接关闭
                        return null;
                    }

                    totalReceived += received;
                    attempts++;//避免无限循环和数据过于切分
                    await Task.Delay(20);
                }
                catch (SocketException ex)
                {
                    attempts++;
                    Console.WriteLine($"Error: {ex.Message}");

                    if (attempts >= MaxAttempts)
                    {
                        return null;
                    }
                }
            }

            return totalReceived == expectedLength ? buffer : null;
        }

        #endregion


    }
}
